/* http://www.andymilne.dial.pipex.com/Scales.shtml
 * is a good site that defines proper and prime scales.  
 * proper means that no interval of size X in a scale is smaller than an interval of size N<X
 * prime means that every note has a major or minor third of another note, and no note has both a major and minor third in the scale 
 */
 
/* more to do: 
1. take in predicates that list notes for each voice,
chord names (i.e. c# minor7), function assignments
(function assignments are specified relative to a key
-- i think it's the only place we need keys), and
cadence structures.

2. determine the chord names consistent with the
specified notes.  

3. for each chord name, determine the list of
functions it could serve (including the key for each
function).

4. determine the actual functions served, according to
progression rules.

5. determine the chord progressions consistent with
those specified

6. determine the specific notes for those chords

7. cad64??? 
*/
 
refNote([[c,''],3]).

numVoices(N) :- N=4.

chordInterpretation([F,K]):-
    functionNameIndex(F,_,_),
    key(K).
    
chordInterpretationList(X):-
    checklist(chordInterpretation,X).

chordName([R,Q,S,I]):-
    chroma(R),
    inversion(I,_,S),
    chordTypeFromToneQualities(_,[Q,S,complete,unextended]).

%should calculate inversion instead of having them hardcoded...
inversion(root,root,triad).
inversion(six,third,triad).
inversion(sixFour,fifth,triad).
inversion(seven,root,seventh).
inversion(sixFive,third,seventh).
inversion(fourThree,fifth,seventh).
inversion(fourTwo,seventh,seventh).
inversion(cad64,root,triad).

/*
%  X is a list of chordNames, each of which is like: [Root, Quality, Size, Inversion]
%  Y is a list of chordInterpretationLists, one for each chord.  
%  each chordInterpretationList lists all interpretations for its chord that apply for its particular context.
%  to be an acceptable progression, each element in Y must be a non-empty list.
*/
progressionInterpretation(X,Y):-
    %make a list of all currently acceptable functions
    findall([F,M,Q,S],(member(M,[[ionian,natural],[aeolian,_]]),scaleName(W,M),chordInScale(W,F,_,[Q,S,complete,unextended])),L),
    getPossibleInterpretations(X,L,Z),  
    listAllTransitions(LT),   
                                                                %try to get rid of this cut -- its may be forcing us to pick only first chord when chords are vars.    
    findActualInterpretation(Z,LT,Y1),!,				%i don't understand why this cut is necessary
    %write('\n\n\n***********'),
    write(Y1),
    getRepeats(Z,Y2),
    getCad64s(Z,Y3),
    mergeAll([Y1,Y2,Y3],[],Y).

getRepeats(_,[]).
    %any chord can be followed by any of its non-cad64 inversions (including repeat) as long as seventh is maintained
    %checkInversion()
    
getCad64s(_,[]).
    %only chord that can follow a cad64 is a repeat or its resolution
    
% for each chordName in X, put a list in Z that is the subset of interpretations from L that have the same quality and size.
getPossibleInterpretations([],_,[]).
getPossibleInterpretations(X,L,Z):-
    X=[A|B],
    getInterpretationList(A,L,I),!,	%why is this cut necessary?  is it hurting us?  without it, we repeat over the same answers...
    write(I),
    write('\n\n'),
    getPossibleInterpretations(B,L,Y),
    Z=[I|Y].
 
getInterpretationList(_,[],[]).
getInterpretationList([R,Q,S],L,I):-
    L=[A|B],
    getInterpretationList([R,Q,S],B,J),  
    findall([F,Inv,[K,M]],(A=[F,M,Q,S],getTonicKey(R,M,F,K),getInversions(F,S,M,Invs),member(Inv,Invs)),J2),
    append(J,J2,I).
    
/* if the chord with root R plays the function F in mode M, K is the chroma for the tonic*/    
getTonicKey(R,M,F,K):-
    scaleName(X,M),
    intervalsForScale(X,I),
    functionNameIndex(F,N,I),    
    nth1(N,I,Int),	% this should be improved...  we're depending on the function index corresponding exactly to the interval size...
    interval(K,R,Int).
    
% X is a list of chordInterpretationLists that contain every possible chordInterpretation for each chord.  
% Y is the same list of chordInterpretationLists, but pared down only to contain those that actually apply.
findActualInterpretation(X,[],Y):-
    write('\n'),write('*******NO MORE TRANSITIONS'),write('\n'),
    length(X,N),
    makeNEmpties(N,Y).
findActualInterpretation(X,[[N,L]|LS],Y):-
    write('\n\ntesting new transition:'),write([N,L]),
    getTransitions(X,L,YI),!,	%why is this cut necessary?
    write('\nfound: '),write(YI),write('\n'),
    findActualInterpretation(X,LS,YS),
    %write('\nDONE\n'),
    mergeLists(YI,YS,Y).	%YI and YS are both lists of length n, Y is also length n, and it's ith item is the union of YI and YS's ith item

makeNEmpties(0,[]).
makeNEmpties(N,Y):-
    N>0,
    M is N-1,
    makeNEmpties(M,Y1),
    Y=[[]|Y1],!.

getTransitions([],_,[]).
getTransitions([A|B],L,I):-
    checkTransition([A|B],L,I1),
    %write('\nfound: '),write(I1),write(' now shifting\n'),
    checkTransition(B,L,I2),
    mergeLists(I1,I2,I).	%I1 and I2 are both lists, and length(I1) >= length (I2).  I is the list of unions of ith components of I1 and I2, starting from the *right*

mergeLists([],A,A).
mergeLists(A,[],A).
mergeLists(A,B,C):-
    length(A,N),
    length(B,M),
    N>=M,
    reverse(A,[A1|AS]),
    reverse(B,[B1|BS]),
    union(A1,B1,C1),
    reverse(AS,AF),
    reverse(BS,BF),
    mergeLists(AF,BF,CF),
    reverse(CF,CS),
    CR=[C1|CS],
    reverse(CR,C).

%checkTransition([],[],[]).
%checkTransition(_,[],[]):-write('reached end of transition.  successful transition match!').
checkTransition(A,B,[]) :- 
        length(A,M),
        length(B,P),
        P>M.
checkTransition([A|_],[X],[Ints]) :-
        findall(Int,(member(X,A),Int=[X]),Ints).    
checkTransition([A|B],[X|Y],Ints) :- 
        length([A|B],M),
        length([X|Y],P),
        M>=P,
        P>1,
        %write('\n\n'),write('testing:'),write(X),
        %findall(Int,(member(X,A),X=Int),I),
        findall(Int,(member(X,A),checkTransition(B,Y,Ints1),Ints1=[C|_],length(C,N),N>0,Int=[[X]|Ints1]/*,write('\n'),write(Int),write('\n')*/),PreInts),
                        %need to change this to not care about natural/melodic/harmonic
        %makeNEmpties(M,E),
        mergeAll(PreInts,[],Ints).
        
mergeAll([],X,X).
mergeAll([A|B],X,M):-
    mergeAll(B,X,M1),
    mergeOne(A,M1,M).

mergeOne([],[],[]).
mergeOne([],L,L):-
    length(L,N),
    N>0.
mergeOne(X,[],L):-
    length(X,N),
    N>0,
    makeNEmpties(N,E),
    mergeOne(X,E,L).
mergeOne([A|B],[X|Y],L):-
    mergeOne(B,Y,L1),
    union(A,X,U),
    L=[U|L1].  
       
/*
checkTransition([A|_],[X|_],[]) :- 
        findall(Int,(member(X,A),X=Int),I),
        length(I,0).
        %write('no matches').
*/

getInversions(F,S,M,I):-
    member(S,[triad,seventh]),
    findall(X,inversion(X,_,S),Invs),
    legalInversions(F,L),
    intersection(Invs,L,Invs2),
    qualityInMode(F,S,M,Q),    
    ((Q=diminished,S=triad) -> subtract(Invs2,[root],I); I=Invs2).    
    
legalInversions(dominant,[root,six,seven,sixFive,fourThree,fourTwo,cad64]).
legalInversions(F,[root,six]):-
    member(F,[tonic,'leading tone',subtonic]).
legalInversions(F,[root]):-
    member(F,[mediant,submediant]).
legalInversions(F,[root,six,seven,sixFive,fourThree,fourTwo]):-
    member(F,[supertonic,subdominant]).

qualityInMode(F,Size,M,Q):-
    scaleName(Scale,M),
    chordInScale(Scale,F,_,[Q,Size,_,_]).
    
listAllTransitions(L):-
    findall([X,Y],transition(X,Y),L).

transition(authenticCadence,[[dominant,_,K],[tonic,_,K]]).
transition(deceptiveCadence,[[dominant,_,K],[submediant,root,K]]).
transition(deceptiveCadence,[[dominant,_,K],[subdominant,six,K]]).
%could decpetive cadence go to ii43 or vii42? (is only restriction that 6 is in bass?)

transition(oneAny,[[tonic,_,K],[_,_,K]]).

transition(sevOne,[['leading tone',six,K],[tonic,_,K]]).
%does the second tonic really have to be different inversion than the first one?
%could this be a subtonic too?

transition(twoFive,[[supertonic,_,K],[dominant,_,K]]).
transition(expandTwo,[[supertonic,_,K],[tonic,six,K],[supertonic,_,K]]).	%only context sensitive case i know of
%can either supertonic be a seventh?  if so, does it really have to be 7 or 65?  does second supertonic really have to be different inversion from first?

transition(fourFive,[[subdominant,_,K],[dominant,_,K]]).
transition(fourTwo,[[subdominant,_,K],[supertonic,_,K]]).
%can IV seventh go to supertonic (or tonic or mediant) or does it have to go to dominant?
transition(fourThree,[[subdominant,six,K],[mediant,root,K]]).	%could IV be a seventh here?
transition(plagalCadence,[[subdominant,_,K],[tonic,_,K]]).
%does plagal have to be root to root?
%does anything special have to happen after IV-6 goes to III or I-6?

transition(sixFour,[[submediant,root,K],[subdominant,_,K]]).
transition(sixTwo,[[submediant,root,K],[supertonic,_,K]]).
transition(sixFive,[[submediant,root,K],[dominant,_,K]]).
transition(sixThree,[[submediant,root,K],[mediant,root,K]]).
transition(sixOne,[[submediant,root,K],[tonic,six,K]]).
%does anything special have to happen after VI goes to III or I-6?

transition(sevThree,[[subtonic,_,K],[mediant,root,K]]).
transition(sevThree,[['leadingTone',six,K],[mediant,root,K]]).
transition(threeFour,[[mediant,root,K],[subdominant,root,K]]).
transition(threeTwo,[[mediant,root,K],[supertonic,six,K]]).	%what about going to ii-65?
transition(threeSix,[[mediant,root,K],[submediant,root,K]]).
transition(threeFive,[[mediant,root,K],[dominant,_,K]]).
%can III expand ii (or V) by substituting for I-6?

%need to handle back relating dominants, I->(IV-6 or VI)->I-6 (or III?) *we don't enforce descending*, plagal cadences may happen in bad spots.
% subtonic (4-6) 5, 4-65 7 root triad (where did these come from?)

/*
always:  
only dominant can be cad64 (does this need to be?  cool to have cad64 behavior elsewhere)
only only non-repeat that can follow a cad64 is its resolution
any chord can be followed by any of its inversions (including repeat) as long as seventh is maintained
only supertonic, subdominant, and dominant can be sevenths
submediant and mediant must be root position
leading tone must be six inversion (no root position diminished chords?)
no 64 inversion triads
*/

/*
Song->NonFinalPeriods,FinalPeriod.
NonFinalPeriods->NonFinalPeriods,NonFinalPeriod | .
NonFinalPeriod->NonFinalPhrases,FinalPhrase.
NonFinalPhrases->NonFinalPhrases,NonFinalPhrase | .
NonFinalPhrase->SemiCadence.
FinalPhrase->ImperfectAuthenticCadence.
FinalPeriod->NonFinalPhrases,EndingPhrase.
EndingPhrase->PerfectAuthenticCadence.

SemiCadence->Transitions,TransitionToFinalDominant.
ImperfectAuthenticCadence->Transitions,TransitionToFinalTonic.
PerfectAuthenticCadence->Transitions,TransitionToEndingTonic.

PAC->DominantHarmony,Tonic|TonicHarmony,DominantHarmony,Tonic.		ends in root pos. V - I, 1 is in the soprano
IAC->DominantHarmony,Tonic|TonicHarmony,DominantHarmony,Tonic.		ends in V - I, at least one of which is inverted, or 1 is not in the soprano
SC->TonicHarmony,DominantHarmony|DominantHarmony.			last dominant is root position V triad

//define PAC, IAC, SC as predicates to transitions, rather than part of the grammar.  check that they occur at the end of phrases and not in the middle.

TonicHarmony->TonicHarmony,DominantHarmony,TonicHarmony|TonicHarmony,Tonic|Tonic.  %add TonicHarmony,DominantHarmony to include back relating dominants (p 153)
TonicHarmony->TonicHarmony,Subdominant(root,triad),TonicHarmony.
TonicHarmony->TonicHarmony(root,triad),Bass6,TonicHarmony(first,triad).  %bass descends from 6 to 3, does not rise
Bass6->[subdominant,first,triad]|[submediant,root,triad].
Tonic->|[tonic,_,triad].
question: can the middle seven be any inversion/size if it's subtonic?

question:mediant?!?!

DominantHarmony->DominantHarmony,TonicHarmony, DominantHarmony |
                PossibleSubmediant,PossiblePreDominantHarmony, PossiblyDelayedDominant |DominantHarmony,Bass6,PreDominantHarmony, PossiblyDelayedDominant.
PossiblyDelayedDominant->[dominant,cadentialSixFour,triad],Dominant|Dominant|Seven.
PossibleSubmediant->Submediant | .
Dominant->Dominant,Bass6,Dominant|[dominant,_,_]|Dominant,[dominant,_,_].  if seventh is introduced, it is not dropped.
Submediant->[submediant,first,triad].	i think this is supposed to be 'root'
Seven->['leading tone',first,triad]|[subtonic,_,_].

1 (7) 3 (4 or 2-6) 5
(1 or 6) 3 (4 or 2-6 or 6) (3 allows decending 7)  what did that mean?
3 substitutes for 1-6
subtonic (4-6) 5
4-65 7 root triad


PossiblePreDominantHarmony->PreDominantHarmony| .
PreDominantHarmony->Supertonic|Subdominant|Subdominant(triad),Supertonic.
Subdominant->[subdominant,_,_]|Subdominant,[subdominant,_,_].  if seventh is introduced, it is not dropped.
Supertonic->Supertonic,[tonic,first,triad],Supertonic|[supertonic,_,_]|Supertonic,[supertonic,_,_].  different inversions on either side of tonic.  if seventh is introduced, it is not dropped.

any chord can be preceded by any inversion of its dominant, or its vii6
any chord can be reinterpreted and treated as playing a new function over a new tonic, as long as both interpretations follow rules above.

all:  unless otherwise specified, inversions are first or second if chord is a triad
*/

midiNum(A,40) :-
	refNote(A).

midiNum(A,N) :-
	refNote(X),
	midiNum(X,R),
	!,
	note(A),
	halfStepsBetween(X,A,D),
	N is R+D.

noteList(L) :-
	numVoices(V),
	listSizeBtw(0,V,L),
	checklist(note,L).

chordTypeFromToneQualities(ToneQualList,[ThirdQual,triad,incomplete,unextended]) :-
	listSizeBtw(2,2,ToneQualList),
	member([perfect,octave],ToneQualList),	/*the list at least contains a perfect octave and some kind of third, and nothing else*/
	member([ThirdQual,third],ToneQualList),
	toneQualityList(ToneQualList).

chordTypeFromToneQualities(ToneQualList,[ChordQual,triad,complete,unextended]) :-
	chordQualityFrom35(ThirdQual,FifthQual,ChordQual),
	listSizeBtw(3,3,ToneQualList),		
	member([perfect,octave],ToneQualList),	/*this list contains a perfect octave, and some kind of third and fifth, and nothing else*/
	member([ThirdQual,third],ToneQualList),
	member([FifthQual,fifth],ToneQualList),
	toneQualityList(ToneQualList).

chordTypeFromToneQualities(ToneQualList,[ChordQual,seventh,complete,unextended]) :-
	chordQualityFrom357(ThirdQual,FifthQual,SeventhQual,ChordQual),
	listSizeBtw(4,4,ToneQualList),
	member([perfect,octave],ToneQualList), 		/*the list contains a perfect octave and some kind of third, fifth, and seventh, and nothing else*/
	member([ThirdQual,third],ToneQualList),
	member([FifthQual,fifth],ToneQualList),
	member([SeventhQual,seventh],ToneQualList),
	toneQualityList(ToneQualList).

chordTypeFromToneQualities(ToneQualList,[ChordQual,seventh,incomplete,unextended]) :-
	chordQualityFrom37(ThirdQual,SeventhQual,ChordQual),
	listSizeBtw(3,3,ToneQualList),
	member([perfect,octave],ToneQualList),	/*the list contains a perfect octave, some kind of third and seventh, and nothing else*/
	member([ThirdQual,third],ToneQualList),
	member([SeventhQual,seventh],ToneQualList),
	toneQualityList(ToneQualList).

chordTypeFromToneQualities(ToneQualList,[ChordQual,seventh,complete,extended]) :-
	chordQualityFrom357(ThirdQual,FifthQual,SeventhQual,ChordQual),
	numVoices(V),
	listSizeBtw(4,V,ToneQualList),
	member([perfect,octave],ToneQualList), 		/*the list contains at least a perfect octave and some kind of third, fifth, and seventh*/
	member([ThirdQual,third],ToneQualList),
	member([FifthQual,fifth],ToneQualList),
	member([SeventhQual,seventh],ToneQualList),
	member(E,ToneQualList),				/*and at least one more tone*/
	E\=[perfect,octave],
	E\=[_,third],
	E\=[_,fifth],
	E\=[_,seventh],
	toneQualityList(ToneQualList).

chordTypeFromToneQualities(ToneQualList,[ChordQual,seventh,incomplete,extended]) :-
	chordQualityFrom37(ThirdQual,SeventhQual,ChordQual),
	numVoices(V),
	listSizeBtw(3,V,ToneQualList),
	member([perfect,octave],ToneQualList),	/*the list contains a perfect octave, some kind of third and seventh, and no fifth*/
	member([ThirdQual,third],ToneQualList),
	member([SeventhQual,seventh],ToneQualList),
	toneQualityList(ToneQualList),
	not(member([_,fifth],ToneQualList)),
	member(E,ToneQualList),				/*and at least one more tone that is not a fifth*/
	E\=[perfect,octave],
	E\=[_,third],
	E\=[_,fifth],
	E\=[_,seventh],
	toneQualityList(ToneQualList).

chordQualityFrom357(ThirdQual,perfect,SeventhQual,ChordQual) :-
	chordQualityFrom37(ThirdQual,SeventhQual,ChordQual).
chordQualityFrom357(minor,diminished,minor,halfdiminished).
chordQualityFrom357(minor,diminished,diminished,diminished).

chordQualityFrom37(major,major,major).
chordQualityFrom37(major,minor,dominant).
chordQualityFrom37(minor,minor,minor).

chordQualityFrom35(major,perfect,major).
chordQualityFrom35(major,augmented,augmented).	
chordQualityFrom35(minor,perfect,minor).
chordQualityFrom35(minor,diminished,diminished).

intsBtw(M,M,L) :-
	M is floor(M),
	L=[M].
intsBtw(M,N,L) :-
	Min is min(M,N),
	Max is max(M,N),
	New is Min+1,
	New =< Max,
	intsBtw(New,Max,A),
	L=[Min|A].

listNoLongerThan(N,L):-
	listSizeBtw(0,N,L).

listSizeBtw(N,M,L) :-
	intsBtw(N,M,I),
	member(A,I),
	length(L,A).

/*
toneQualityList([A]):-
	interval(A).
*/
toneQualityList(L) :-
	/*a list of at most numVoices intervals, increasing in size, with no dupes, and only one quality present for a given size*/
	numVoices(V),
	listNoLongerThan(V,L),
	checklist(interval,L),
	increasingNoDupes(L,interval,intervalCompare),
	noSizeMatches(L).

%noSizeMatches([]).
noSizeMatches([A]):-
	interval(A).
noSizeMatches([A,B|C]):-
	interval(A),
	interval(B),
	A=[_,SA],
	B=[_,SB],
	SA\=SB,
	noSizeMatches([A|C]),
	noSizeMatches([B|C]).

intervalCompare(D,A,B) :-
	interval(A),
	interval(B),
	A=[_,AS],
	B=[_,BS],
	intervalNameIndexNumHalfStepsType(AS,AN,_,_),
	intervalNameIndexNumHalfStepsType(BS,BN,_,_),
	getCompareSymbol(D,AN,BN).

getCompareSymbol(=,A,A):-
	number(A).
getCompareSymbol(>,A,B):-
	number(A),
	number(B),
	A>B.
getCompareSymbol(<,A,B):-
	number(A),
	number(B),
	A<B.

increasingNoDupes([],_,_).
increasingNoDupes([A],Type,_):-
	apply(Type,[A]).
increasingNoDupes([A,B|C],Type,Compare) :-
	apply(Type,[A]),
	apply(Type,[B]),
	Test=..[Compare,<,A,B],
	call(Test),
/*
	apply(Compare,A,AN),
	apply(Compare,B,BN),
	BN > AN,
*/
	%increasingNoDupes([A|C],Type,Compare), /*unnecessary, right?*/
	increasingNoDupes([B|C],Type,Compare).

/*
smaller(A,[]) :-
	interval(A).
smaller(A,[B|C]) :-
	interval(A),
	interval(B),
	numHalfSteps(A,NA),
	numHalfSteps(B,NB),
	NB > NA,
	smaller(A,C).
*/

numHalfSteps(I,N):-
	interval(I),
	I=[Q,S],
	intervalNameIndexNumHalfStepsType(S,_,H,_),
	qualityFromSizeAndDiff(S,D,Q),
	N is mod(12+D+H,12).	/*should derive this 12*/

isAnInterval(R,C):-
	chroma(R),
	chroma(C),
	interval(R,C,_).

knownSubset([],K,K).
knownSubset([A|AT],K,F):-
	not(ground(A)),
	knownSubset(AT,K,F).
knownSubset([A|AT],K,F):-
	ground(A),
	knownSubset(AT,[A|K],F).

allInList([],_,_).
allInList([A|AT],R,B):-
	interval(R,A,I),
	member(I,B),
	allInList(AT,R,B).

/*this is really tricky.  chromaList(L) and toneQualityList(I), both of length N.
 *this thing succeeds if the chromas in L are exactly the intervals over R specified in I (no extras on either side).
 *and either L or I or both can be unbound!  in that case we'll make all the L's and I's for which it holds.
 *the number of toneQualityLists is much smaller than the number of chromaLists, because toneQualityLists are restricted to have only one member of each interval size.
 *so if both I and L are unbound, we should key off toneQualityList and perform no worse than generating all toneQualityLists (in 4 voices it's 2708 of them, takes ~2 secs).
 *i've got it pretty close, but for some reason it still takes about three times as long to do this function, perhaps cuz of the recurssion?
 */
chromasAndIntervalsCorrespond([],_,[],0).
chromasAndIntervalsCorrespond([L],R,[I],1):-
%	write(-),
	interval(R,L,I).
chromasAndIntervalsCorrespond(L,R,I,N):-
	N>1,

%	write(N),

	chroma(R),
	length(L,N),
	length(I,N),

	M is N-1,

	knownSubset(L,[],KL),
	allInList(KL,R,I),

	toneQualityList(I),

	I=[NewI|SmallerI],


%%	L=[NewC|SmallerL],

%	member(NewC,L),
%	member(NewI,I),

	interval(R,NewC,NewI),
	chroma(NewC),
	interval(NewI),

	length(SmallerL,M),	

%	select(NewC,L,SmallerL),
%	select(NewI,I,SmallerI),

	NewI=[_,ISize],

	chromasAndIntervalsCorrespond(SmallerL,R,SmallerI,M),

	not(member(NewC,SmallerL)),
	not(member([_,ISize],SmallerI)),

	NewL=[NewC|SmallerL],
	NewIS=[NewI|SmallerI],
	
	makeIncreasingNoDupes(NewL,chroma,chromaCompare,SortedL),
	makeIncreasingNoDupes(NewIS,interval,intervalCompare,SortedI),

	toneQualityList(SortedI),
	chromaList(SortedL),
	
	SortedL=L,
	SortedI=I.

toneQualityList(L,R,ToneQualList) :-
	numVoices(V),
	intsBtw(0,V,WS),
	member(W,WS),
	length(L,W),
	length(ToneQualList,W),

	chroma(R),
	chromasAndIntervalsCorrespond(L,R,ToneQualList,W),

	chromaList(L),
	toneQualityList(ToneQualList).

	%makeIncreasingNoDupes(SL,chroma,chromaCompare,SSL),
	%subset(SSL,L),

/*
	chromaList(L),
	checklist(isAnInterval(R),L),							%make sure everything in L is an interval over R (eg. Ab to Ax isn't)
	findall(I,(member(N,L),interval(R,N,I)),Candidate),				%find all intervals over R in L
*/
/*
	noSizeMatches(Candidate),							%ensure only one quality per size
*/

/*
	makeIncreasingNoDupes(Candidate, interval, intervalCompare, UnkeyedSortedCandidate),

*/
/*
	list_to_set(Candidate,UnsortedCandidate),					%remove dupes
	noSizeMatches(UnsortedCandidate),						%ensure only one quality per size
	findall(-(S,X),(member(X,UnsortedCandidate),X=[_,XS],intervalNameIndexNumHalfStepsType(XS,S,_,_)	%%numHalfSteps(X,S)
													),KeyedCandidate),	%key each interval by its interval size index   %%number of halfsteps
	keysort(KeyedCandidate,SortedCandidate),					%sort by these sizes
	findall(Q,member(-(_,Q),SortedCandidate),UnkeyedSortedCandidate),		%strip off the keys
*/
/*
	toneQualityList(UnkeyedSortedCandidate),					%ensure we wound up with a well formed toneQualityList
	UnkeyedSortedCandidate=ToneQualList.
*/

makeIncreasingNoDupes(L,Type,Compare,M) :-
	checklist(Type,L),
	list_to_set(L,UnsortedSet),
	predsort(Compare,UnsortedSet,Candidate),	/*this must remove anything considered equal by Compare!  that's bad.*/
	length(UnsortedSet,LN),
	length(Candidate,LN),				/*so we'll make sure we didn't lose any*/
	increasingNoDupes(Candidate,Type,Compare),
	Candidate=M.

chordName(C,[R,T/*,Fig,FuncList*/]) :-
	chromaList(C,L),
	member(R,L),
	toneQualityList(L,R,ToneQualList),
	chordTypeFromToneQualities(ToneQualList,T).
/*
	figure(C,Fig),
	function(C,FuncList).
*/

functionList([]).
functionList([A|B]):-
	A=[F,K],
	key(K),
	function(F),
	functionList(B).	

key([R,M]):-
	chroma(R),
	mode(M).

mode(X):-
	scaleName(_,X).

toneSize(a2,3).
toneSize(whole,2).
toneSize(half,1).

isToneSize(X):-
	toneSize(X,_).

countOccurances(_,[],0).
countOccurances(X,[A|B],N):-
	X=A,
	countOccurances(X,B,M),
	N is 1+M.
countOccurances(X,[A|B],N):-
	X\=A,
	countOccurances(X,B,N).

tonesSumTo([],0).
tonesSumTo([A|B],N):-
	toneSize(A,M),
	tonesSumTo(B,P),
	N is M+P.

fundamentalScale([whole,whole,half,whole,whole,whole,half]).	/*the major scale, represented in whole and half steps.  all others are derived from this.*/

precededBy(_,_,[]).
precededBy(X,_,[A]):-
	X\=A.
precededBy(X,Y,[A,B|C]):-
	X\=A,
	X\=B,
	precededBy(X,Y,[B|C]).
precededBy(X,Y,[A,B|C]):-
	X=B,
	Y=A,
	precededBy(X,Y,C).

/* i used to consider melodicable to mean (1st def) only the 6th would have to be raised to remove any a2's.  
 * but now i think we should let melodicable mean (2nd def) any amount of raising necessary to remove a2's.
 * talk to jared about this definition.  should we really require that scales be harmonicable and melodicable at all?
 * for instance, locrian (7th mode of major) is not melodicable by 1st def -- raising 6 and 7 will leave an a2 between 5 and 6! (discovered by this program)
 * also, modes 6 and 7 of melodic minor have the exact same issue.  it happens whenever the natural ends in three whole steps.
 * 
 * in 2nd def, the melodicTransform keeps pushing the a2 back until it hits a half step (and it has to), but doesn't this change the nature of the scale by a lot?
 * that's probably ok, since we still get the original scale, and the mutilated thing is just the melodic transform, which can be kind of far away anyway.
 * OK, that's what we're doing.
 *
 * it may be acceptable in classical not to use locrian ('theoretical mode can't feel like tonic'), but it's used in jazz over halfdiminished chords.
 * for melodic minor harmony, (arguing modes 6 and 7 should be available) see:
 * http://www.yavapai.cc.az.us/ycmusic.nsf/089813187917b677072567b1004e54ac/4451f2d2c922d55b072567b9006510d3?OpenDocument
 * http://www.outsideshore.com/primer/primer/ms-primer-4-3.html
 *
 * note to self:  automatically detect avoid notes!
 */

/*there are only 21 of these for a 12 halfstep octave*/
naturalScale(X):-
	scale(X),
	fundamentalScale(F),
	length(F,N),
	length(X,N),
	countOccurances(a2,X,0).

/*i no longer think these should be required to be a natural scale -- in any case, doesn't affect a 12 halfstep octave*/
/*
	harmonicable(X),	%can only be a scale if it is already harmonic or can be made to be
	melodicable(X).		%can only be a scale if it is already melodic or can be made to be
*/

functionNameIndex(tonic,1,_).
functionNameIndex(supertonic,2,_).
functionNameIndex(mediant,3,_).
functionNameIndex(subdominant,4,_).
functionNameIndex(dominant,5,_).
functionNameIndex(submediant,6,_).
functionNameIndex(subtonic,7,Ints):-
    member([minor,seventh],Ints).        
functionNameIndex('leading tone',7,Ints):-
    member([major,seventh],Ints).

%don't know how to get at chords whose roots are not diatonic to their scale yet...
%i think you can only be a IIb chord  if your underlying scale has a major second, for example.
%does that mean minor modes have III# chords?
/*
functionNameIndex('supertonic b',2,Ints).
functionNameIndex('mediant b',3,Ints).
functionNameIndex('subdominant b',4,Ints).   
functionNameIndex('subdominant #',4,Ints).
functionNameIndex('dominant b',5,Ints).
functionNameIndex('dominant #',5,Ints).
functionNameIndex('submediant b',6,Ints).
*/

/*ok, here's where we are:  trying to get constructToneQualList/chordInScale to work...*/

/*cool queries:  
 *
 * print all named chords that are functions of named scales
 * naturalScale(X),findall(N,scaleName(X,N),L),findall(I,intervalsForScale(X,I),M),findall([Function,Qual,Size],chordInScale(X,Function,ToneQualList,[Qual,Size,complete,unextended]),Q).
 *
 * print a report of the frequency of unnamed chords in all the functions of our named scales
 * findall(ToneQualList,(scale(X),findall(N,scaleName(X,N),L),length(L,W),W>1,functionInScale(X,_,ToneQualList,_),not(chordTypeFromToneQualities(ToneQualList,_))),Q),countDupes(Q,[],Report),sort(Report,Sorted).

 * according to this last one, our problem chords are:
[[5, [[perfect, octave], [major, third], [augmented, fifth], [minor, seventh]]], 
 [5, [[perfect, octave], [major, third], [diminished, fifth]]], 
 [5, [[perfect, octave], [major, third], [diminished, fifth], [minor, seventh]]], 
 [7, [[perfect, octave], [major, third], [augmented, fifth], [major, seventh]]], 
 [12, [[perfect, octave], [minor, third], [perfect, fifth], [major, seventh]]]]

 *note, we never have +5 d7; m3 +5; m3 d5 M7; M3 d5 M7; any d7 problems 
 *check to see if these unnamed chords have same intervalic structure as some other quality chords... or what chords have same intervalic structure in general.

 */

countDupes([],X,X).
countDupes([A|AS],C,T):-
	member([N,A],C),
	select([N,A],C,Rest),
	M is N+1,
	countDupes(AS,[[M,A]|Rest],T).
countDupes([A|AS],C,T):-
	not(member([_,A],C)),
	countDupes(AS,[[1,A]|C],T).

/*we are going to wind up with chords we can't name.  for example, in harmonic aeolian, I7 is minor third/major seventh, and III7 is augmented fifth/major seventh.*/

/*so perhaps we should come up with names for:
 *triad: minor 3, augmented 5
 *	 major 3, diminished 5
 *sevenths: minor 3, major 7 (and all kinds of 5's)
 *	    augmented 5 (and all kinds of 7's and 3's)
 *	    major 3, diminished 5 (and all kinds of 7's)
 *	    diminished 7 (with all kinds of 3's and 5's)
 */	    

/*
chordInScale(Scale,Function,ToneQualList,[ChordQuality,ChordSize,complete,unextended]):-
	intervalsForScale(Scale,Ints),
	member([SeventhQual,seventh],Ints),
	length(Scale,N),
	intsBtw(1,N,NS),
	member(FunctionIndex,NS),
	functionNameIndex(Function,FunctionIndex,SeventhQual),
	applyMode(FunctionIndex,Scale,NewScale),
	intervalsForScale(NewScale,NewInts),	
	constructToneQualList(NewInts, ChordSize, ToneQualList),
	chordTypeFromToneQualities(ToneQualList,[ChordQuality,ChordSize,complete,unextended]).
*/

chordInScale(Scale,Function,ToneQualList,[ChordQuality,ChordSize,complete,unextended]):-
	functionInScale(Scale,Function,ToneQualList,ChordSize),
	chordTypeFromToneQualities(ToneQualList,[ChordQuality,ChordSize,complete,unextended]).

/*this works, but depends on being able to write out the intlist (unique sizes for each interval).  better to run of actual interval sizes in half steps, so we don't depend on that.*/
functionInScale(Scale,Function,ToneQualList,ChordSize):-
	intervalsForScale(Scale,Ints),
	length(Scale,N),
	intsBtw(1,N,NS),
	member(FunctionIndex,NS),
	functionNameIndex(Function,FunctionIndex,Ints),
	applyMode(FunctionIndex,Scale,NewScale),
	intervalsForScale(NewScale,NewInts),	
	constructToneQualList(NewInts, ChordSize, ToneQualList).

constructToneQualList(Ints, triad, ToneQualList):-
	member([perfect,octave],Ints),
	member([ThirdQual,third],Ints),
	member([FifthQual,fifth],Ints),
	ToneQualList=[[perfect,octave],[ThirdQual,third],[FifthQual,fifth]].

constructToneQualList(Ints, seventh, ToneQualList):-
	member([perfect,octave],Ints),
	member([ThirdQual,third],Ints),
	member([FifthQual,fifth],Ints),
	member([SeventhQual,seventh],Ints),
	ToneQualList=[[perfect,octave],[ThirdQual,third],[FifthQual,fifth],[SeventhQual,seventh]].
	

/*tie together intervals and scales*/
intervalsForScale(S,I):-
	fundamentalScale(F),
	tonesSumTo(F,N),
        intsBtw(1,N,NS),
        member(M,NS),
        length(S,M),
	reverse(S,R),
        length(R,M),
	intervalsForScale(R,[],I,N).
intervalsForScale([],IS,IS,0).
intervalsForScale([S|SS],IS,I,N):-
	N>0,
	toneSize(S,T),
	D is N-T,
	numHalfSteps(Int,D),
	Int=[_,ISize],
	not(member([_,ISize],IS)),
	intervalsForScale(SS,[Int|IS],I,D).

/*doesn't handle extension -- would need to know what the extension was
 *also, what should happen for triads -- does aug/dim/maj/min triad say anything about seventh to choose in scale?
 */
scaleForChord([Q,Size,_,_],S):-
	naturalScale(S),
	chordTypeFromToneQualities(A,[Q,Size,complete,unextended]),
	intervalsForScale(S,I),
	subset(A,I).

/*this is the right way to define scale -- we divide an octave into any number of toneSized steps*/
/*the longest scale is all halfsteps -- the chromatic scale*/
/*includes pentatonic, blues, wholetone, wholehalfs, etc.*/
/*for an octave of 12 halfsteps, there are ~1000 ways to do this.*/
scale(X):-
	fundamentalScale(F),
	tonesSumTo(F,M),
	intsBtw(1,M,NS),
	member(N,NS),
	length(X,N),
	checklist(isToneSize,X),
	tonesSumTo(X,M).

harmonicable(X):-
	harmonicScale(X).
harmonicable(X):-
	applyHarmonicTransform(X,_).
	
melodicable(X):-
	melodicScale(X).
melodicable(X):-
	applyMelodicTransform(X,_).

harmonicScale(X):-
	scale(X),
	containsLeadingTone(X).

melodicScale(X):-
	scale(X),
	harmonicScale(X),	/*have to be harmonic (have leading tone) to be melodic*/
	countOccurances(a2,X,0).

modeDegree(ionian,1).
modeDegree(dorian,2).
modeDegree(phrygian,3).
modeDegree(lydian,4).
modeDegree(mixolydian,5).
modeDegree(aeolian,6).
modeDegree(locrian,7).

scaleName(S,[N,natural]):-
	fundamentalScale(F),
	modeDegree(N,M),
	applyMode(M,F,S).

scaleName(S,[N,harmonic]):-
	scaleName(S1,[N,natural]),
	applyHarmonicTransform(S1,S),
	S\=S1.

scaleName(S,[N,melodic]):-
	scaleName(S1,[N,natural]),
	applyHarmonicTransform(S1,H),
	applyMelodicTransform(H,S),
	H\=S1,
	S\=H,
	S\=S1.

/*this is enough to name all 21 natural scales in 12 halfsteps!
 *note, these scales must have 5 steps and 2 half steps
 *so the half steps can either be adjacent (modes of phrygian/locrian melodics), separated by one whole step (modes of aeolian melodic), or two whole steps (modes of ionian)
 *they cannot be separated by 3 whole steps, because then they will only be separated by 2 on the other side!
 *
 *the modes of phyrigian melodic and locrian melodic are the same, and as far as i know, a new discovery that arises from my definitions for natural, harmonic, and melodic
 *note, they're equivalent to whole tone scales with a passing tone at one of the degrees.
 *the second and seventh such modes do not have spellings that use a unique interval size for each degree.
 *
 *i'm assuming that harmonic scales that aren't melodic (they contain a2's) are not suitable for "modalizing", and that we only want to "modalize" one level after the modes of ionian.
 *not true!  fully diminished seventh chords can only come from a mode of a harmonic that puts the a2 at the end!
 */
scaleName(S,[M,N,T]):-
	scaleName(S1,[N,T]),
	applyMode(M,S1,S),
	M\=1,
	T\=natural.

containsLeadingTone(S):-
	reverse(S,[A|_]),
	A=half.

applyHarmonicTransform(S,S):-
	harmonicScale(S).
/*S has no leading tone, so raise the last scale degree to a halfstep away from the first scale degree
 *this also means increasing the penultimate interval, so it must be small enough to make up the difference without becoming larger than a2
 */
applyHarmonicTransform(S,H):-
	%write('\napplying harmonic to'), write(S),
	reverse(S,[A,B|C]),
	A\=half,
	toneSize(A,L),		/*L is either 2 or 3, since A has to be a whole or a2*/
	toneSize(half,P),	/*P will be 1*/
	Q is L-P,		/*Q is either 1 or 2, and it's how much the penultimate note has to make up for this shift*/

	toneSize(B,N),
	M is N+Q,
	toneSize(New,M),	/*the new penultimate note must not be bigger than a2*/

	reverse([half,New|C],Temp),	
	harmonicScale(Temp),		/*this will make sure that we still have a scale.*/
	H=Temp.	

find(X,[J|K],A,B):-
	X=J,
	A=[],
	B=K.
find(X,[J|K],A,B):-
	X\=J,
	find(X,K,NewA,B),
	append([J],NewA,A).

fixA2s(X,Y):-
	reverse(X,R),
	fixA2sR(R,[],T),
	reverse(T,Y).

largeInterval(a2).
largeInterval([N]):-
	N>3.
small(half).
small(whole).

/*ok these are crazy cuz i'm exhausted, but the idea is that for every a2, there has to be a half somewhere in front of it that can be made into a whole*/
fixA2sR([],Y,Y).
fixA2sR([A|B],Y,Z):-
	small(A),
	append(Y,[A],T),
	fixA2sR(B,T,Z).
fixA2sR([a2,Next|B],Y,Z):-
	toneSize(Next,S),
	M is S+1,
	toneSize(New,M),	/*only works if Next wasn't also a2*/
	append(Y,[whole],T),
	fixA2sR([New|B],T,Z).
fixA2sR([a2,a2|B],Y,Z):-
	append(Y,[whole],T),
	toneSize(a2,N),
	M is N+1,		/*making a new large*/
	fixA2sR([[M]|B],T,Z).
fixA2sR([[X],Next|B],Y,Z):-
	largeInterval([X]),
	toneSize(Next,S),
	toneSize(whole,D),
	M is S+X-D,
	toneSize(New,M),	/*only works when X was 4 and Next was half*/
	append(Y,[whole],T),
	fixA2sR([New|B],T,Z).
fixA2sR([[X],Next|B],Y,Z):-
	largeInterval([X]),
	toneSize(Next,S),
	toneSize(whole,D),
	M is S+X-D,
	append(Y,[whole],T),
	fixA2sR([[M]|B],T,Z).

applyMelodicTransform(S,S):-
	melodicScale(S).

applyMelodicTransform(S,M):-
	not(harmonicScale(S)),
	applyHarmonicTransform(S,H),
	melodicScale(H),
	M=H.

applyMelodicTransform(S,M):-
	not(harmonicScale(S)),
	applyHarmonicTransform(S,H),
	not(melodicScale(H)),
	applyMelodicTransform(H,M).

/*push a2 back, replacing with wholes, until it hits a half*/
applyMelodicTransform(S,M):-
	harmonicScale(S),		/*have to be harmonic to be melodic*/
	member(a2,S),
	fixA2s(S,Temp),
	melodicScale(Temp),
	M=Temp.

applyMode(M,S1,S2) :-
	scale(S1),
	length(S1,L),
	intsBtw(1,L,PossibleModes),
	member(M,PossibleModes),
	N is M-1,
	shift(S1,N,Test),
	scale(Test),
	S2 = Test.

shift(S1,0,S2):-
	S1=S2.
shift(S1,N,S2):-
	N>0,
	S1=[A|B],
	append(B,[A],Temp),
	M is N-1,
	shift(Temp,M,S2).
shift(S1,N,S2):-
	N<0,
	reverse(S1,R),
	R=[A|B],
	append(B,[A],RTemp),
	reverse(RTemp,Temp),
	M is N+1,
	shift(Temp,M,S2).

%modeType(natural,0).	/*the unaltered modes will be called natural type modes*/
%modeType(harmonic,1).	/*for each modeDegree, some can raise their sevenths to make a leading tone (dorian, phrygian, mixolydian, aeolian, locrian) -- call this a harmonic type mode*/
%modeType(melodic,2).	/*some of these can also raise their sixth to avoid an A2 and make a step below leading tone (phyrgian, aeolian, locrian) -- call this a melodic type mode*/

/*question -- is this enough to express all ways to have 7 wholesteps and halfsteps make an octave?*/
/*the 'jazz scales' are modes of melodic aeolian -- do we have to take modes of each of our modes?*/
/*question -- do triads/sevenths built on these cause us to need more than our 9 types?*/

function(F):-
	member(F,[tonic,'minor supertonic?',supertonic,'minor mediant?',mediant,subdominant,'tritone?',dominant,'minor mediant?',submediant,subtonic,leadingTone]).

bassChroma(C,B) :-
	noteList(C),
	lowestNote(C,[B,_]).
	/*B is the lowest note in C*/

lowestNote([N],N).
lowestNote([A,B],L) :-
	midiNum(A)<midiNum(B) -> L=A ; L=B.
lowestNote([A,B|C],L) :-
	lowestNote([A,B],D),
	lowestNote([D|C],L).

/*dynamically compute "figure" by looking at intervals over bass.*/
/*
figure(C,Key,Figure) :-
	bassChroma(C,B),
	interval(R,B,I),
	I=[perfect,octave];I=[_,third];I=[_,fifth];I=[_,seventh].

function(ChordName,Key,Function) :-
	chordName(ChordName),
	Key=[R,M],
	chroma(R),
	mode(M),
*/
	/*make sure no DLT*/

/*chroma lists should be ordered, with no dupes*/
chromaList([]).
chromaList(L) :-
	numVoices(V),
	listSizeBtw(0,V,L),
	increasingNoDupes(L,chroma,chromaCompare).

chromaIndex(C,N):-
	chroma(C),
	C=[S,A],
	spellingNameIndexOffset(S,N1,_),
	accidentalOffset(A,N2),
	%numSpellings(M),
	numAccidentals(P),
	N is P*N1+N2.

chromaCompare(D,A,B):-
	chroma(A),
	chroma(B),
	chromaIndex(A,AN),
	chromaIndex(B,BN),
	getCompareSymbol(D,AN,BN).

numAccidentals(M):-
	findall(N,accidentalOffset(N,_),L),
	list_to_set(L,Q),
	length(Q,M).

numSpellings(M):-
	findall(N,spellingNameIndexOffset(N,_,_),L),
	list_to_set(L,Q),
	length(Q,M).

maxInList([A],A):-
	number(A).
maxInList([A|B],N):-
	number(A),
	maxInList(B,N1),
	N is max(A,N1).	

chromaList(C,L) :-
	noteList(C),
	findall(X,(member([X,_],C),chroma(X)),L1),
	makeIncreasingNoDupes(L1,chroma,chromaCompare,L).
	/*make a chroma list from the notes in C*/

chord(C) :-
	numVoices(N),
	length(C,N),
	noteList(C),
	chordName(C,[R,T]).
/*
	spacingOK(C),
	doublingOK(C,R).

spacingOK(C) :-

doublingOK(C,R) :-
*/
	/*there is at most one of any dissonant interval over R, and at most one of any dissonant interval over the bass*/
		/*i think this may be wrong -- diminished chords can have two d5's over the R*/
	/*if there is no fifth, there are strictly more roots than anything else*/

/*N is negative if A is higher than B*/
halfStepsBetween(A,B,N) :-
	note(A),
	note(B),
	[AChroma,AOctave] = A,
	[BChroma,BOctave] = B,

	chromaOffset(AChroma,AOffset),
	chromaOffset(BChroma,BOffset),

	OctaveSize=12,	/*I'd like this to be derived generally*/
	N is OctaveSize*(BOctave-AOctave)+BOffset-AOffset.	/*test this -- not sure it works around octave breaks*/

/*there is ambiguity taking into account where the octave numbering breaks are!*/
/*notice the different numbering schemes below: */
/* c1 d1 e1 f1 g1 a2 b2 c2  |  a2 is below c2! */
/* c2 d2 e2 f2 g2 a2 b2 c3  |  a2 is above c2! */
/*so we will use the convention that the breaks are at a (the first scheme above), so that a higher letter in same octave means a higher note*/
note([Chroma,Octave]) :-
	octaveNum(Octave),
	chroma(Chroma).

/*a chroma is a note without an octave -- so not a specific note on the piano, but the quality of say, Bb in any octave*/
/*we're going to limit to double flats/sharps*/
chroma([Spelling,Accidental]) :-
	spellingNameIndexOffset(Spelling,_,_),	/*require that we know this note spelling (a-g)*/
	accidentalOffset(Accidental,_).

chromaOffset(Chroma,Offset) :-
	chroma(Chroma),
	[Spelling,Accidental]=Chroma,
	spellingNameIndexOffset(Spelling,_,Base),
	accidentalOffset(Accidental,D),	
	Offset is D+Base.

accidentalOffset('',0).
accidentalOffset(b,-1).
accidentalOffset(#,1).
accidentalOffset(x,2).
accidentalOffset(bb,-2).

/*A and B are chroma -- not notes -- so no octaves here*/
/*we're looking for the interval from A UP to B*/
/*change this to use halfStepsBetween above...*/
interval(A,B,I) :-
	chroma(A),	/*require that A and B are well formulated chroma*/
	chroma(B),

	/*determine interval size (octave, second, etc.) by inspecting the note names*/
	[ASpell|_]=A,
	[BSpell|_]=B,
	spellingNameIndexOffset(ASpell,Index1,_),
	spellingNameIndexOffset(BSpell,Index2,_),
	/* abs(Index2-Index1,Diff), */
	/* spellingNameIndexOffset(_,Index,_) (ListOfNoteNames), */ /*not sure how to dynamically get a list of all possible note names*/
	/* NumNoteNames = length(ListOfNoteNames), */		/*should be 7 -- i like the general formulation here, but could be timesuck to derive every time*/
	NumNoteNames=7,
	Diff is Index2-Index1+NumNoteNames,
	IntervalNameIndex is mod(Diff,NumNoteNames),	
	intervalNameIndexNumHalfStepsType(Size,IntervalNameIndex,MajorNumHalfSteps,_),	

	/*determine interval quality*/	
	/*first, determine the actual number of halfsteps up from A to B*/
	chromaOffset(A,NA),
	chromaOffset(B,NB),
	OctaveSize=12,	/*I'd like this to be derived generally*/
	ActualNumHalfSteps is mod(NB-NA+OctaveSize,OctaveSize),

	/*then, determine the difference between the actual number and the number for a major (or perfect) interval between the note names we're working with*/
	Difference is ActualNumHalfSteps-MajorNumHalfSteps,
	fixDiff(Difference,FixedDifference), /*nasty hack -- how can i get rid of this?*/

	/*then, we can characterize the quality based on the size of the interval and how its number of halfsteps differs from a major interval of that size*/
	qualityFromSizeAndDiff(Size,FixedDifference,Quality),

	I = [Quality,Size],
	interval(I).

/*hack*/
fixDiff(11,-1).
fixDiff(A,B) :-
	A \=11,
	B is A.

qualityFromSizeAndDiff(S,0,major) :-
	imperfectSize(S).

qualityFromSizeAndDiff(S,0,perfect) :-
	perfectSize(S).

qualityFromSizeAndDiff(S,1,augmented) :-
	perfectSize(S).

qualityFromSizeAndDiff(second,1,augmented).
qualityFromSizeAndDiff(seventh,-2,diminished).

qualityFromSizeAndDiff(S,-1,minor) :-
	imperfectSize(S).

qualityFromSizeAndDiff(S,-1,diminished) :-
	perfectSize(S).

interval([A,B]) :-
	perfectSize(B),
	member(A,[diminished,augmented,perfect]).

interval([A,B]) :-
	imperfectSize(B),
	member(A,[major,minor]).

interval([diminished, seventh]).

interval([augmented, second]).

imperfectSize(S) :-
	intervalNameIndexNumHalfStepsType(S,_,_,imperfect).

perfectSize(S) :-
	intervalNameIndexNumHalfStepsType(S,_,_,perfect).

intervalNameIndexNumHalfStepsType(octave,0,0,perfect).
intervalNameIndexNumHalfStepsType(second,1,2,imperfect).
intervalNameIndexNumHalfStepsType(third,2,4,imperfect).
intervalNameIndexNumHalfStepsType(fourth,3,5,perfect).
intervalNameIndexNumHalfStepsType(fifth,4,7,perfect).
intervalNameIndexNumHalfStepsType(sixth,5,9,imperfect).
intervalNameIndexNumHalfStepsType(seventh,6,11,imperfect).

spellingNameIndexOffset(a,0,0).
spellingNameIndexOffset(b,1,2).
spellingNameIndexOffset(c,2,3).
spellingNameIndexOffset(d,3,5).
spellingNameIndexOffset(e,4,7).
spellingNameIndexOffset(f,5,8).
spellingNameIndexOffset(g,6,10).

octaveNum(A):-
	intsBtw(1,7,L),
	member(A,L).
	
/*testers*/
:- interval([b,b],[a,''],[major,seventh]).
:- interval([a,''],[f,#],[major,sixth]).
:- interval([f,#],[c,''],[diminished,fifth]).
:- interval([d,b],[c,''],[major,seventh]).
:- interval([d,b],[c,b],[minor,seventh]).
:- interval([d,b],[c,bb],[diminished,seventh]).
:- interval([d,bb],[c,bb],[minor,seventh]). /*prolog discovered this!  kind of funky, but i think it's what we want.*/
:- not(interval([e,#],[f,''],I)),interval(I).
:- interval([e,#],[f,#],[minor,second]).
:- interval([e,#],[f,x],[major,second]).
:- interval([e,''],[f,x],[augmented,second]).
:- not(interval([e,b],[f,x],I)),interval(I).
:- interval([g,''],[b,''],[major,third]).
:- not(interval([g,''],[b,#],I)),interval(I).
:- interval([g,''],[b,b],[minor,third]).
:- not(interval([g,''],[b,bb],I)),interval(I).
:- interval([f,b],[f,''],[augmented,octave]).
:- interval([a,''],[a,b],[diminished,octave]).
:- interval([a,''],[a,''],[perfect,octave]).
:- interval([b,b],[f,''],[perfect,fifth]).

/*good queries*/
/* show every defined midinote and all the note names that correspond to it
findAll(-(B,A),(midiNum(A,B)),L),keysort(L,M),setof(Q,(member(A-Q,M)),Z),R=[A,Z].
*/

/*why do these not halt?*/
/*refNote(X),midiNum(X,N).*/
/*midiNum(A,N).*/
